<!DOCTYPE html>
<body onload="doTest()">
<pre id="output">
</pre>
<img src="kraken.jpg" id="image">
<canvas id="original" style="width: 3264px; height: 2448px"></canvas>
<canvas id="copy" style="width: 3264px; height: 2448px"></canvas>
<canvas id="indirectCopy" style="width: 3264px; height: 2448px"></canvas>
<canvas id="scaledUp" style="width: 6528px; height: 4896px"></canvas>
<canvas id="scaledDown" style="width: 1632px; height: 1224px"></canvas>
<canvas id="rotated" style="width: 2448px; height: 3264px; border: 1px solid red;"></canvas>
<script>
var width = 3264;
var height = 2448;
var original = document.getElementById("original");
var originalContext = original.getContext("2d");
var idleTimer = 20;

function log(text) {
    if (typeof text != "string" )
        text = text.toString();
    var span = document.createElement("span");
    document.getElementById("output").appendChild(span);
    span.innerHTML = text.replace(/</g, "&lt;").replace("/>/g", "&gt;").replace("/&/g", "&amp")+ "<br>";
}

function shouldBe(_a, _b) {
    if (typeof _a != "string" || typeof _b != "string")
        log("WARNING: shouldBe() expects string arguments.");
    var _aValue = eval(_a);
    var _bValue = eval(_b);
    if (_aValue !== _bValue)
        log("FAILURE: " + _a + "should have been " + _bValue + ". Instead it was " + _aValue + ".");
}

function flushOperation(context) {
   var imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
}

function timeCanvasOperation(context, canvasOperation) {
    var repetitions = 2;
    var startDate = new Date();

    for (var i = 0; i < repetitions; ++i) {
        canvasOperation();
        flushOperation(context);
    }
    var finishDate = new Date();
    var timeWithOverhead = finishDate - startDate;

    for (var i = 0; i < repetitions; ++i) {
        flushOperation(context);
    }
    var overheadTime = new Date() - finishDate;
    return (timeWithOverhead - overheadTime) / repetitions;
}

function doTest() {
    var image =  document.getElementById("image");
    image.width = width;
    image.height = height;
    
    original.width = width;
    original.height = height;
    original.getContext("2d").drawImage(image, 0, 0);
    window.setTimeout(doCopy, idleTimer);
}

function doCopy() {
    log("===== Copy/Scale/Rotate Tests =====");

    var copy =  document.getElementById("copy");
    copy.width = width;
    copy.height = height;
    var copyContext = copy.getContext("2d");
    copyContext.globalCompositeOperation = "copy";
    var time = timeCanvasOperation(copyContext,
        function () {
            copyContext.drawImage(original, 0, 0);
        });
    log("Direct image copy: " + time + "ms");
    window.setTimeout(doIndirectCopy, idleTimer);
}

function doIndirectCopy() {
    var indirectCopy =  document.getElementById("indirectCopy");
    indirectCopy.width = width;
    indirectCopy.height = height;
    var indirectCopyContext = indirectCopy.getContext("2d");
    indirectCopyContext.globalCompositeOperation = "copy";
    time = timeCanvasOperation(indirectCopyContext,
        function () {
            var imageData = originalContext.getImageData(0, 0, width, height);
            indirectCopyContext.putImageData(imageData, 0, 0);
        });
    log("Indirect copy with (via ImageData): " + time + "ms");
    window.setTimeout(doScaleUp, idleTimer);
}

function doScaleUp() {
    var scaledUp =  document.getElementById("scaledUp");
    scaledUp.width = width * 2;
    scaledUp.height = height * 2;
    var scaledUpContext = scaledUp.getContext("2d");
    scaledUpContext.globalCompositeOperation = "copy";
    time = timeCanvasOperation(scaledUpContext,
        function () {
            scaledUpContext.drawImage(original, 0, 0, width * 2, height * 2);
        });
    log("Copy with 2x scale: " + time + "ms");
    shouldBe("document.getElementById('scaledUp').width", "width * 2");
    shouldBe("document.getElementById('scaledUp').height", "height * 2");
    window.setTimeout(doScaleDown, idleTimer);
}

function doScaleDown() {
    var scaledDown =  document.getElementById("scaledDown");
    scaledDown.width = width / 2;
    scaledDown.height = height / 2;
    var scaledDownContext = scaledDown.getContext("2d");
    scaledDownContext.globalCompositeOperation = "copy";
    time = timeCanvasOperation(scaledDownContext,
        function () {
            scaledDownContext.drawImage(original, 0, 0, width / 2, height / 2);
        });
    log("Copy with 0.5x scale: " + time + "ms");
    shouldBe("document.getElementById('scaledDown').width", "width / 2");
    shouldBe("document.getElementById('scaledDown').height", "height / 2");
    window.setTimeout(doRotation, idleTimer);
}

function doRotation() {
    var rotated = document.getElementById("rotated");
    rotated.width = height;
    rotated.height = width;
    var rotatedContext = rotated.getContext("2d");
    rotatedContext.globalCompositeOperation = "copy";
    rotatedContext.rotate(Math.PI / 2);

    time = timeCanvasOperation(rotatedContext,
        function () {
            rotatedContext.drawImage(original, 0, -original.height);
        });
    log("Copy with rotate: " + time + "ms");
    shouldBe("document.getElementById('rotated').width", "height");
    shouldBe("document.getElementById('rotated').height", "width");
    log("");
    window.setTimeout(doStrokeTextTests, idleTimer);
}

function doStrokeTextTests() {
    log("===== StrokeText Tests =====");
    var strokeTextFunc = function(canvas, str, x, y) { canvas.strokeText(str, x, y); };
    doNextTextTest(strokeTextFunc, doFillTextTests, 1);
}

function doFillTextTests() {
    log("===== FillText Tests =====");
    var fillTextFunc = function(obj, str, x, y) { obj.fillText(str, x, y); };
    doNextTextTest(fillTextFunc, doSmallStrokeLineTests, 1);
}

function doNextTextTest(writeText, nextFunc, numStrings) {
    var canvas = document.createElement('canvas');
    canvas.width = 100;
    canvas.height = 550;
    document.body.appendChild(canvas);
    var ctx = canvas.getContext('2d');

    var time = timeCanvasOperation(ctx,
        function () {
            for (var i = 0; i < 10; i++)
                for (var j = 0; j < numStrings; j++)
                    writeText(ctx, "Printing text!", 25, 500 * j / numStrings + 25);
        });
    log(numStrings + " strings: " + (time / 10) + "ms");

    numStrings *= 10;
    if (numStrings < 1001)
        window.setTimeout(function () {
                              doNextTextTest(writeText, nextFunc, numStrings);
                          }, idleTimer);
    else {
        log("");
        window.setTimeout(nextFunc, idleTimer);
    }
}

function doSmallStrokeLineTests() {
    log("===== StrokeLine Tests =====");
    doNextStrokeLineTest(doLargeStrokeLineTests, 1, 150, 150);
}

function doLargeStrokeLineTests() {
    doNextStrokeLineTest(null, 1, 1000, 1000);
}

function doNextStrokeLineTest(nextFunc, numLines, xSize, ySize) {
    var canvas = document.createElement('canvas');
    canvas.width = xSize;
    canvas.height = ySize;
    document.body.appendChild(canvas);
    var ctx = canvas.getContext('2d');

    var time = timeCanvasOperation(ctx,
        function() {
            for (var i = 0; i < 10; i++) {
                ctx.beginPath();
                if (numLines == 1) {
                    ctx.moveTo(5, 5);
                    ctx.lineTo(xSize - 1, ySize - 1);
                } else {
                    for (var j = 0; j < numLines; j++) {
                        ctx.moveTo(j * xSize / numLines, j * ySize / numLines);
                        ctx.lineTo(xSize - 5, 5);
                    }
                }
                ctx.stroke();
                ctx.closePath();
            }
        });
    log("Stroking " + numLines + " lines in " + xSize + "x" + ySize + ": " + (time / 10) + "ms");
    
    numLines *= 10;
    if (numLines < 1001)
        window.setTimeout(function () {
                              doNextStrokeLineTest(nextFunc, numLines, xSize, ySize);
                          }, idleTimer);
    else {
        log("");
        if (nextFunc)
            window.setTimeout(nextFunc, idleTimer);
        else
            log("DONE!");
    }
}
</script>

</body>
